package gu.sql2java.excel.utils;

import static com.gitee.l0km.com4j.basex.bean.BeanPropertySupport.isEmpty;
import static com.gitee.l0km.com4j.basex.bean.jdk.BeanPropertySupportImpl.BEAN_SUPPORT;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

public class MethodSupport {

	/**
	 * 字符串首字母大写
	 * @param name
	 */
	private static String firstUpperCase(String name){
		if(Strings.isNullOrEmpty(name))return name;
		char[] list = name.toCharArray();
		list[0] = Character.toUpperCase(list[0]);
		return new String(list);
	}

	/**
	 * 返回注解类的所有方法名,输入参数为{@code null}返回空表
	 * @param annotationType
	 */
	public static List<String> methodNamesOf(Class<? extends Annotation> annotationType) {
		if(null != annotationType){
			return Lists.transform(Arrays.asList(annotationType.getDeclaredMethods()),Method::getName);
		}
		return Collections.emptyList();
	}

	/**
	 * 返回注解类的所有方法名,输入参数为{@code null}返回空表
	 * @param annotation
	 */
	public static List<String> methodNamesOf(Annotation annotation) {
		if(null != annotation){
			return methodNamesOf(annotation.annotationType());
		}
		return Collections.emptyList();
	}
	/**
	 * 调用{@code name} 指定字段的写方法setXXX<br>
	 * 当存在多个重载方法时自动查找最匹配的方法
	 * @param object 调用对象
	 * @param name 字段名
	 * @param value 字段值
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 */
	public static void invokeWriteMethod(Object object,String name,Object value) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{
		String methodName = "set" + firstUpperCase(name);
		Iterable<Method> writeMethods = Iterables.filter(Arrays.asList(object.getClass().getMethods()),m->{
			Class<?>[] parameterTypes = m.getParameterTypes();
			return methodName.equals(m.getName()) && parameterTypes.length ==1 
					&&  (null == value ||m.getParameterTypes()[0].isInstance(value));
			});
		if(Iterables.isEmpty(writeMethods)){
			throw new NoSuchMethodError("NOT FOUND write method for " + name);
		}
		Iterator<Method> itor = writeMethods.iterator();
		Method first = itor.next();
		while(itor.hasNext()){
			Method m = itor.next();
			if(first.getParameterTypes()[0].isAssignableFrom(m.getParameterTypes()[0])){
				first = m;
			}
		}
		first.invoke(object, value);
	}
	/**
	 * 调用{@code name} 指定字段的读方法setXXX<br>
	 * @param object 调用对象
	 * @param name 字段名
	 * @return 读取的值
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @throws SecurityException 
	 * @throws NoSuchMethodException 
	 */
	public static Object invokeReadMethod(Object object,String name) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException{
		String methodName = "get" + firstUpperCase(name);
		return object.getClass().getMethod(methodName).invoke(object);
	}
	/**
	 * 将left中有定义的字段复制到to<br>
	 * 输入参数为{@code null}时忽略
	 * @param annotationType 定义字段名的注解类
	 * @param from
	 * @param to
	 * @return always to
	 */
	public static <T>T mergeAnnotaionFields(Class<? extends Annotation> annotationType,T from,T to){
		if(null!=from && null != to && null != annotationType){
			Map<String, PropertyDescriptor> props = BEAN_SUPPORT.getProperties(from.getClass(), 3, true);
			Arrays.asList(annotationType.getDeclaredMethods()).forEach(m->{
				String name = m.getName();
				Object defaultValue = m.getDefaultValue();
				try {
					PropertyDescriptor descriptor = props.get(name);
					if(null == descriptor){
						throw new NoSuchMethodException(String.format("Unknown property '%s' on class '%s'",name,from.getClass().getName()));
					}
					Object value = descriptor.getReadMethod().invoke(from);
					if(!equalDefault(defaultValue, value)){
						//net.gdface.utils.SimpleLog.log("merge field {}={}",name,value);
						descriptor.getWriteMethod().invoke(to,value);
					}
				} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
					//net.gdface.utils.SimpleLog.log(e.getMessage());
				}
			});
		}
		return to;
	}
	/**
	 * 比较注解默认值与指定的值是否相等
	 * @param defaultValue
	 * @param value
	 * @return
	 */
	private static boolean equalDefault(Object defaultValue,Object value){
		if(Objects.equal(defaultValue, value)){
			return true;
		}
		if(isEmpty(defaultValue) && isEmpty(value)){
			return true;
		}
		if(defaultValue.getClass().isArray()){
			if(value.getClass().isArray()){
				return arrayEquals(defaultValue,value);				
			}
			return false;
		}
		if(String.valueOf(defaultValue).equals(String.valueOf(value))){
			return true;
		}
		
		return false;
	}
	private static boolean arrayEquals(Object arr1, Object arr2){
	    Class<?> c = arr1.getClass();
	    if (!c.getComponentType().isPrimitive()) {
	        c = Object[].class;
	    }
		try {
			return (Boolean) Arrays.class.getMethod("equals", c, c).invoke(null, arr1, arr2);
		} catch (Exception e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		}
	}
}
